#
#
#   repParser.py - Parseing a XML-report definition
#
#   Copyright (C) 2004
#
#   This library is free software; you can redistribute it and/or
#   modify it under the terms of the GNU Lesser General Public
#   License as published by the Free Software Foundation; either
#   version 2.1 of the License, or (at your option) any later version.
#
#   This library is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#   Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public
#   License along with this library; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""Portal forms __init__.
$Id: repParser.py,v 0.1 2004/10/29 20:19:18 wbauer Exp $
"""
__version__='$Revision: 0.1 $'[11:-2]

import string
import imp
import sys
import os

from reportlab.lib import colors
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.units import toLength
from reportlab.lib import pagesizes
from error import errorPrint

import repPlatypus
import exceptions
import xml.dom.minidom



graphic_objects = ['fixedimage','rectangle','roundrect','ellipse','polygon','line','string','infostring','customshape']
content_objects = ['parameters','sources','section','groups']
page_sequence_values = ['all','even','odd']
section_types = ['reportheader','reportfooter','groupheader','groupfooter','detail','detailheader','detailfooter','pageheader','pagefooter']
parameter_type = ['string','int','date','money','real']
parameter_origin = ['command','function','default','input','sum','key']
alignment_enum = {'left': TA_LEFT,
		  'right':TA_RIGHT,
		  'center':TA_CENTER,
		  'justify':TA_JUSTIFY,
		 }

_marker = None

db_types = ["mysql","CSV","postgres"]

def unescape(s):
	if '&' not in s:
		return s
	s = s.replace("&lt;", "<")
	s = s.replace("&gt;", ">")
	s = s.replace("&apos;", "'")
	s = s.replace("&quot;", '"')
	s = s.replace("&amp;", "&") # Must be last
	return s

def create_rep(template_file, db_data, folder):
	'''
	creates a pdf file declared in template_file.
	db_data is a directory {'host':val,'user':val,'password': val, 'database': val,'filename':val}
		which is used, if there is no data for db-access declared in <datasource>
	folder is the folder, where the template is stored
	'''
	filename = os.path.join(folder, template_file)

	try:
		file = open(filename,'r')
	except exceptions.IOError:
		error.errorPrint('EPAR001',(filename))

	#read template file
	document_xml_template = file.read()
	file.close()
	xml_dom = xml.dom.minidom.parseString(document_xml_template)
	doc= ReportParser(xml_dom, "iso-8859-1")
	doc.report.build(db_data)
	return



class NullResourceHandler:
	''' basic resource Handler '''

	def __init__(self):
		pass

	def get(self,url):
		return url


class Parser:
	''' base class'''

	xml_attributes = {}

	def __init__(self,dom,encoding,resourceHandler=None):
		self.dom = dom
		self.encoding = encoding
		self.resourceHandler = resourceHandler or NullResourceHandler()
		self.report = self._create()

	def xml_arg(self,node,name):
		if name in node.attributes.keys():
			v = str(node.getAttribute(name))
		else:
			if self.xml_attributes.has_key(node.nodeName):
				v = self.xml_attributes[node.nodeName][name]
			else:
				v = None
		return v

	def xml_ceval(self,node,name):
		if name in node.attributes.keys():
			v = str(node.getAttribute(name))
		else:
			if self.xml_attributes.has_key(node.nodeName):
				v = self.xml_attributes[node.nodeName][name]
			else:
				return None
		return eval(v)

	def xml_unit(self,node,name):
		val = self.xml_arg(node,name)
		if val is None:
			return 0
		return toLength(val)

	def xml_unit_list(self,node,name):
		val = self.xml_arg(node,name)
		if val is None:
			return eval('(0,0)')
							# val should be in the form (x1,y1),(x2,y2),(x3,y3)
		val2 = string.replace(val,' ','')	# eliminate all blanks
		valtupel = string.split(val,')')	# make tupel split with ) -> l gives ['(x1,y1',',(x2,y2',',x3,y3',...]
		if valtupel[-1] == '':			# last tupel is '' --> remove
			valtupel = valtupel[:-1]
		value = []
		try:
			for v in valtupel:
				v1=string.lstrip(v)		# v has the form:  ,(x1,y1
								# strip it
				v2=string.rstrip(v1)
				if v2[0] == ',':
					v2 = v2[1:]		#remove , if first character
				if v2[0] == '(':
					v3=v2[1:]		#remove (
				else:
					raise AttributeError	# wrong string
				x,y = string.split(v3,',')	# split coordinates
				x1 = toLength(x)		# get points from cm
				y1 = toLength(y)
				value.append("( %s,%s)" % (x1,y1))	# make (x1,y1)
			value1 = eval(string.join(value,','))		# compute [(x1,y1),(x2,y2),(x3,y3)]
			return value1
		except:
			error.errorPrint('EPAR002',(name,val))

	def __call__(self):
		return self.doc.create()

	def _createObject(self,node):
		pass

	def _create(self):
		pass




class ReportParser(Parser):
	''' parses a report definition file'''

	xml_attributes = {'report': {
				'filename':'out.pdf',
				'author':'',
				'version':'1.0',
				'description':'',
				'name':''
			        },
			'parameter': {
				'name':'',
				'required':'0',
				'limited':'0',
				'default':'',
				'description':'',
				'origin':'',	# possible values are: command|function|default|input|sum|key
				'typ':'',
				'function':'',
				'column':'',
				'group':''
				},
			'datasource': {
				'name':'',
				'host':'',
				'user':'',
				'password':'',
				'table':'',
				'database':'',
				'master':'',
				'masterlink':'',
				'detaillink':'',
				'sort':'',
				'typ':'',
				'sql':'',
				'keyname':'' # name of table or parameter with mappingvalues (column:origin,value)
				},
			'group': {
				'name':'',
				'column':'',
				'header':'',
				'footer':''
				},
			'layout': {
				'pagesize':'A4',
				'landscape':'0',
				'showboundary':'0',
				'leftmargin':'0',
				'topmargin':'0',
				'rightmargin':'0',
				'bottommargin':'0',
				'allowsplitting':'1',
				'pagesequence':'all'     # all|even|odd
				},


			'section': {
				'name':'',
				'description':'',
				'source':'',
				'typ':'detail', #reportheader|reportfooter|pageheader|pagefooter|groupheader|groupfooter|detailheader|detailfooter|detail
				'bgcolor':'(1,1,1)',
				'alternate':'0',
				'tableform':'0',
				'newpage':'0'
				},





			'field': {
				'name':'',
				'x':'0',
				'y':'0',
				'color':'(0,0,0)',
				'font':'Times-Roman',
				'fontsize':'12',
				'align':'left',
				'length':'0',
				'height':'0',
				'showboundary':'0',
				'leftpadding':'0',  #this is ignored
				'toppadding':'0',  #this is ignored
				'rightpadding':'0',  #this is ignored
				'bottompadding':'0',  #this is ignored
				'column':'',		# _columnName means, that column is taken from old_record
				'text':'',		# if text begins with '_' it means an parameter
				'typ':'string',    #string|int|real|money|date   like parameter.typ
				'key':'0'	# =1 -> take column-value as key, to get real value
				},

			'rectangle': {
				'name':'',
				'x':'0',
				'y':'0',
				'width':'100',
				'height':'100',
				'fill':'None',
				'stroke':'(0,0,0)',
				'linewidth':'0'
				},
			'roundrect': {
				'name':'',
				'x':'0',
				'y':'0',
				'width':'100',
				'height':'100',
				'radius':'6',
				'fill':'None',
				'stroke':'(0,0,0)',
				'linewidth':'0'

				},
			'line': {
				'name':'',
				'x1':'0',
				'y1':'0',
				'x2':'100',
				'y2':'100',
				'stroke':'(0,0,0)',
				'linewidth':'0'

				},
			'ellipse': {
				'name':'',
				'x1':'0',
				'y1':'0',
				'x2':'100',
				'y2':'100',
				'stroke':'(0,0,0)',
				'fill':'None',
				'linewidth':'0'

				},
			'polygon': {
				'name':'',
				'points':'(0,0),(50,0),(25,25)',
				'stroke':'(0,0,0)',
				'linewidth':'0',
				'fill':'None'

				},
			'fixedimage': {
				'name':'',
				'filename':'',
				'x':'0',
				'y':'0',
				'width':'0',
				'height':'0'
				}

			 }


	def _createObject(self,node):
		rhandler = self.resourceHandler
		obj = None
		if node.nodeName == 'report':
			obj = repPlatypus.xmlReport()
			obj.name = self.xml_arg(node,'name')
			obj.filename = self.xml_arg(node,'filename')
			obj.author = self.xml_arg(node,'author')
			obj.version = self.xml_arg(node,'version')
			obj.description = self.xml_arg(node,'description')
		elif node.nodeName == 'parameter':
			obj = repPlatypus.xmlParameter()
			obj.name = self.xml_arg(node,'name')
			obj.required = self.xml_ceval(node,'required')
			obj.limited = self.xml_ceval(node,'limited')
			obj.default = self.xml_arg(node,'default')
			obj.description = self.xml_arg(node,'description')
			obj.origin = self.xml_arg(node,'origin')
			if obj.origin not in parameter_origin:
				error.errorPrint('EPAR003',(obj.origin, obj.name))

			obj.function = self.xml_arg(node,'function')
			obj.typ = self.xml_arg(node,'typ')
			if obj.typ not in parameter_type:
				print "Error: Wrong type '%s' in Parameter %s !" % (obj.typ,obj.name)
				sys.exit(1)
			obj.column = self.xml_arg(node,'column')
			obj.group = self.xml_arg(node,'group')

		elif node.nodeName == 'datasource':
			obj = repPlatypus.xmlDatasource()
			obj.name = self.xml_arg(node,'name')
			obj.host = self.xml_arg(node,'host')
			obj.user = self.xml_arg(node,'user')
			obj.password = self.xml_arg(node,'password')
			obj.table = self.xml_arg(node,'table')
			obj.database = self.xml_arg(node,'database')
			obj.master = self.xml_arg(node,'master')
			obj.masterlink = self.xml_arg(node,'masterlink')
			obj.detaillink = self.xml_arg(node,'detaillink')
			obj.sort = self.xml_arg(node,'sort')
			obj.keyname = self.xml_arg(node,'keyname')
			obj.typ = self.xml_arg(node,'typ')

			if obj.typ == "":
				obj.typ = "mysql"
			if obj.typ not in db_types:
				error.errorPrint('EPAR004',(obj.typ))

			obj.sql = self.xml_arg(node,'sql')

			if obj.master != "":
				if obj.masterlink == "" or obj.detaillink == "":
					error.errorPrint('EPAR005',(obj.name))


		elif node.nodeName == 'group':
			obj = repPlatypus.xmlGroup()
			obj.name = self.xml_arg(node,'name')
			obj.column = self.xml_arg(node,'column')
			obj.header = self.xml_arg(node,'header')
			obj.footer = self.xml_arg(node,'footer')
			if obj.column == "":  # column must not be empty
					error.errorPrint('EPAR006',(obj.name))

			if obj.header == "" and obj.footer == "":  # header and footer must not be empty
					error.errorPrint('EPAR007',(obj.name))

		elif node.nodeName == 'layout':
			obj = repPlatypus.xmlLayout()
			obj.pagesize = self.xml_arg(node,'pagesize')
			try:
				a=eval("pagesizes."+obj.pagesize)
			except AttributeError:
				error.errorPrint('EPAR008',(obj.pagesize))

			obj.landscape = self.xml_ceval(node,'landscape')
			obj.showboundary = self.xml_ceval(node,'showboundary')
			obj.leftmargin = self.xml_unit(node,'leftmargin')
			obj.rightmargin = self.xml_unit(node,'rightmargin')
			obj.topmargin = self.xml_unit(node,'topmargin')
			obj.bottommargin = self.xml_unit(node,'bottommargin')
			obj.allowsplitting = self.xml_ceval(node,'allowsplitting')
			obj.pagesequence = self.xml_arg(node,'pagesequence')
			if obj.pagesequence not in page_sequence_values:
				error.errorPrint('EPAR009',(obj.pagesequence))

		elif node.nodeName == 'section':
			obj = repPlatypus.xmlSection()
			obj.name = self.xml_arg(node,'name')
			obj.description = self.xml_arg(node,'description')
			obj.source = self.xml_arg(node,'source')
			obj.typ = self.xml_arg(node,'typ')
			if obj.typ not in section_types:
				error.errorPrint('EPAR010', (obj.typ))

			obj.bgcolor = self.xml_ceval(node,'bgcolor')
			obj.alternate = self.xml_ceval(node,'alternate')
			obj.tableform = self.xml_ceval(node,'tableform')
			obj.newpage = self.xml_ceval(node,'newpage')

		elif node.nodeName == 'field':
			obj = repPlatypus.xmlField(
				self.xml_unit(node,'x'),
				self.xml_unit(node,'y')
				)
			obj.name = self.xml_arg(node,'name')
			obj.color = self.xml_ceval(node,'color')
			obj.font = self.xml_arg(node,'font')
			obj.fontsize = self.xml_ceval(node,'fontsize')
			obj.showboundary = self.xml_ceval(node,'showboundary')
			obj.align = alignment_enum.get(self.xml_arg(node,'align'))
			obj.length = self.xml_unit(node,'length')
			obj.height = self.xml_unit(node,'height')
			obj.column = self.xml_arg(node,'column')
			obj.text = self.xml_arg(node,'text')
			obj.key = self.xml_arg(node,'key')
			obj.typ = self.xml_arg(node,'typ')
			if obj.typ not in parameter_type:
				error.errorPrint('EPAR012',(obj.typ))



		elif node.nodeName == 'fixedimage':
			obj = repPlatypus.xmlFixedImage()
			obj.name = self.xml_arg(node,'name')
			obj.filename = self.xml_arg(node,'filename')
			obj.x = self.xml_unit(node,'x')
			obj.y = self.xml_unit(node,'y')
			obj.width = self.xml_unit(node,'width')
			obj.height = self.xml_unit(node,'height')
			obj.size = obj.y + obj.height

		elif node.nodeName == 'rectangle':
			obj = repPlatypus.xmlRectangle(
				self.xml_unit(node,'x'),
				self.xml_unit(node,'y'),
				self.xml_unit(node,'width'),
				self.xml_unit(node,'height')
				)
			obj.name = self.xml_arg(node,'name'),
			obj.fillColor = self.xml_ceval(node,'fill')
			obj.strokeColor = self.xml_ceval(node,'stroke')
			obj.lineWidth = self.xml_unit(node,'linewidth')
			obj.size = obj.y + obj.height + obj.lineWidth

		elif node.nodeName == 'roundrect':
			obj = repPlatypus.xmlRoundRect(
				self.xml_unit(node,'x'),
				self.xml_unit(node,'y'),
				self.xml_unit(node,'width'),
				self.xml_unit(node,'height'),
				self.xml_unit(node,'radius')
				)
			obj.name = self.xml_arg(node,'name')
			obj.fillColor = self.xml_ceval(node,'fill')
			obj.strokeColor = self.xml_ceval(node,'stroke')
			obj.lineWidth = self.xml_unit(node,'linewidth')
			obj.size = obj.y + obj.height + obj.lineWidth

		elif node.nodeName == 'line':
			obj = repPlatypus.xmlLine(
				self.xml_unit(node,'x1'),
				self.xml_unit(node,'y1'),
				self.xml_unit(node,'x2'),
				self.xml_unit(node,'y2')
				)
			obj.name = self.xml_arg(node,'name')
			obj.strokeColor = self.xml_ceval(node,'stroke')
			obj.lineWidth = self.xml_unit(node,'linewidth')
			obj.size = max(obj.y1,obj.y2) + obj.lineWidth/2
		elif node.nodeName == 'ellipse':
			obj = repPlatypus.xmlEllipse(
				self.xml_unit(node,'x1'),
				self.xml_unit(node,'y1'),
				self.xml_unit(node,'x2'),
				self.xml_unit(node,'y2')
				)
			obj.name = self.xml_arg(node,'name')
			obj.fillColor = self.xml_ceval(node,'fill')
			obj.strokeColor = self.xml_ceval(node,'stroke')
			obj.lineWidth = self.xml_unit(node,'linewidth')
			obj.size = max(obj.y1,obj.y2) + obj.lineWidth

		elif node.nodeName == 'polygon':
			obj = repPlatypus.xmlPolygon(self.xml_unit_list(node,'points'))
			obj.name = self.xml_arg(node,'name')
			obj.fillColor = self.xml_ceval(node,'fill')
			obj.strokeColor = self.xml_ceval(node,'stroke')
			obj.lineWidth = self.xml_unit(node,'linewidth')
			max_size = 0
			for p in obj.points:
				(x,y) = p
				if max_size < y:
					max_size = y
			obj.size = max_size


		else:
			raise AttributeError, "Unknown NodeName: %s" % node.nodeName
			error.errorPrint('WPAR001',(node.nodeName))


		return obj

	def _create(self):

		nodesRoot = self.dom.firstChild
		current_obj = self._createObject(nodesRoot)
		rootObject = current_obj
		obj = current_obj

		for nodesL1 in nodesRoot.childNodes:
			if nodesL1.nodeName == 'parameters':
				for nodesL2 in nodesL1.childNodes:
					if nodesL2.nodeName == 'parameter':
						rootObject.parameters.append(self._createObject(nodesL2))
			elif nodesL1.nodeName == 'sources':
				for nodesL2 in nodesL1.childNodes:
					if nodesL2.nodeName == 'datasource':
						rootObject.sources.append(self._createObject(nodesL2))

			elif nodesL1.nodeName == 'groups':
				for nodesL2 in nodesL1.childNodes:
					if nodesL2.nodeName == 'group':
						rootObject.groups.append(self._createObject(nodesL2))
			elif nodesL1.nodeName == 'layout':
				rootObject.layout = self._createObject(nodesL1)
				for nodesL2 in nodesL1.childNodes:
					if nodesL2.nodeName == 'section':
						current_obj = self._createObject(nodesL2)
						for nodesL3 in nodesL2.childNodes:
							if nodesL3.nodeName == 'field':
								current_obj.fields.append(self._createObject(nodesL3))
							elif nodesL3.nodeName == 'fixedimage':
								current_obj.fields.append(self._createObject(nodesL3))
							elif nodesL3.nodeName == 'rectangle':
								current_obj.fields.append(self._createObject(nodesL3))
							elif nodesL3.nodeName == 'roundrect':
								current_obj.fields.append(self._createObject(nodesL3))
							elif nodesL3.nodeName == 'line':
								current_obj.fields.append(self._createObject(nodesL3))
							elif nodesL3.nodeName == 'ellipse':
								current_obj.fields.append(self._createObject(nodesL3))
							elif nodesL3.nodeName == 'polygon':
								current_obj.fields.append(self._createObject(nodesL3))
						obj.sections.append(current_obj)


		return obj


